package com.ltl.project.upload_client.utils;

import com.ltl.project.upload_client.utils.Base64Utils;
import org.apache.commons.io.IOUtils;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.Key;
import java.security.SecureRandom;

/**
 * AES加密解密工具包
 *
 * @author IceWee
 * @version 1.0
 * @date 2012-5-18
 */
public class AESUtils {

    /**
     * 加密算法
     */
    private static final String ALGORITHM = "AES";
    /**
     * 密钥的长度
     */
    private static final int KEY_SIZE = 128;

    /**
     * 生成随机密钥
     *
     * @return
     * @throws Exception
     */
    public static String getSecretKey() throws Exception {
        return getSecretKey(null);
    }

    /**
     * 根据种子生成密钥
     *
     * @param seed 密钥种子
     * @return
     * @throws Exception
     */
    private static String getSecretKey(String seed) throws Exception {

        // 生成 asc 密钥
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
        SecureRandom secureRandom;
        if (seed != null && !"".equals(seed)) {
            secureRandom = new SecureRandom(seed.getBytes());
        } else {
            secureRandom = new SecureRandom();
        }
        keyGenerator.init(KEY_SIZE, secureRandom);
        SecretKey secretKey = keyGenerator.generateKey();
        return Base64Utils.encode(secretKey.getEncoded());
    }

    /**
     * AES文件流加密，并保存到 filePath
     *
     * @param key             AES密钥
     * @param fileInputStream 待加密的文件流
     * @param filePath        文件的保存路径
     * @throws Exception
     */
    public static void encryptFile(String key, InputStream fileInputStream, String filePath) throws Exception {

        Key k = toKey(Base64Utils.decode(key));
        byte[] raw = k.getEncoded();
        SecretKeySpec secretKeySpec = new SecretKeySpec(raw, ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        // 文件流加密
        CipherInputStream cin = new CipherInputStream(fileInputStream, cipher);

        BufferedInputStream in = null;
        BufferedOutputStream out = null;

        in = new BufferedInputStream(cin);
        out = new BufferedOutputStream(new FileOutputStream(filePath));
        IOUtils.copy(in, out);

        cin.close();
        in.close();
        out.close();
    }

    /**
     * AES解密文件
     *
     * @param key             AES密钥
     * @param fileInputStream 待加密的文件的文件流
     * @return 加密后的文件流
     * @throws Exception
     */
    public static InputStream decryptFile(String key, InputStream fileInputStream) throws Exception {

        Key k = toKey(Base64Utils.decode(key));
        byte[] raw = k.getEncoded();
        SecretKeySpec secretKeySpec = new SecretKeySpec(raw, ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        CipherInputStream cin = new CipherInputStream(fileInputStream, cipher);

        return cin;
    }

    /**
     * AES加密数据
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data, String key) throws Exception {
        Key k = toKey(Base64Utils.decode(key));
        byte[] raw = k.getEncoded();
        SecretKeySpec secretKeySpec = new SecretKeySpec(raw, ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * 数据解密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data, String key) throws Exception {
        Key k = toKey(Base64Utils.decode(key));
        byte[] raw = k.getEncoded();
        SecretKeySpec secretKeySpec = new SecretKeySpec(raw, ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * 转换密钥
     *
     * @param key
     * @return
     * @throws Exception
     */
    private static Key toKey(byte[] key) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
        return secretKey;
    }
}
